\documentclass{article}
\title{Pull Parsing in C\# and Java}
\author{jfang}
\begin{document}
\maketitle
\section{C\# Pull Parsers}
In my first article in this series, I wrote about porting a SAX application called RSSReader to the new Microsoft .NET Framework XmlReader. After publication, I received a message from Chris Lovett of Microsoft suggesting I revisit the subject. As he said, while the code I presented works, my approach was not optimal for the .NET framework; I was still thinking in terms of SAX event driven state machinery. A much easier way to approach this problem is to take advantage of the fact that XmlReader does not make you think this way; and, thus, to write a recursive descent RSS transformer as outlined below.
\\
Based on Chris' suggestions, I've also made some other changes, including changing the output mechanism to use the XmlTextWriter, which will take care of generating well formed XHTML on the output side.
\\
And following all that, in a reversal of our usual process, I'll port this code back to Java.
\\
Here then, without further ado, is the new RSSReader, optimized for C\#. I've given the entire listing here, follows by an explanation.
\begin{verbatim}
using System;
using System.IO;
using System.Xml;
using System.Net;

public class RSSReader
{
  public static void Main(string [] args){
    // create an instance of RSSReader
    RSSReader rssreader = new RSSReader();

    try {
      string url = args[0];
      XmlTextWriter writer = new XmlTextWriter(Console.Out);
      writer.Formatting = Formatting.Indented;
      HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url);
      WebResponse resp = wr.GetResponse();
      Stream stream = resp.GetResponseStream();
      XmlTextReader reader = new XmlTextReader(stream);
      reader.XmlResolver = null; // ignore the DTD
      reader.WhitespaceHandling = WhitespaceHandling.None;
      rssreader.RSSToHtml(reader, writer);
    } catch (XmlException e) {
      Console.WriteLine(e.Message);
    }
  }

  public void RSSToHtml(XmlReader reader, XmlWriter writer) {
    reader.MoveToContent();
    if (reader.Name == "rss") {
      writer.WriteStartElement("html");
      while (reader.Read() &&
        reader.NodeType != XmlNodeType.EndElement) {
        switch (reader.LocalName) {
        case "channel":
          ChannelToHtml(reader, writer);
          break;
        case "item":
          ItemToHtml(reader, writer);
          break;
        default: // ignore image and textinput.
          break;
        }
      }
      writer.WriteEndElement();
    } else {
      // not an RSS document!
    }
  }

  void ChannelToHtml(XmlReader reader, XmlWriter writer) {
    writer.WriteStartElement("head");
    // scan header elements and pick out the title.
    reader.Read();
    while (reader.Name != "item" &&
      reader.NodeType != XmlNodeType.EndElement) {
      if (reader.Name == "title") {
        writer.WriteNode(reader, true); // copy node to output.
      } else {
        reader.Skip();
      }
    }
    writer.WriteEndElement();

    writer.WriteStartElement("body");
    // transform the items.
    while (reader.NodeType != XmlNodeType.EndElement) {
      if (reader.Name == "item") {
        ItemToHtml(reader, writer);
      }
      if (!reader.Read())
        break;
    }
    writer.WriteEndElement();
  }

  void ItemToHtml(XmlReader reader, XmlWriter writer) {
    writer.WriteStartElement("p");

    string title = null, link = null, description = null;
    while (reader.Read() &&
      reader.NodeType != XmlNodeType.EndElement) {
      switch (reader.Name) {
      case "title":
        title = reader.ReadString();
        break;
      case "link":
        link = reader.ReadString();
        break;
      case "description":
        description = reader.ReadString();
        break;
      }
    }
    writer.WriteStartElement("a");
    writer.WriteAttributeString("href", link);
    writer.WriteString(title);
    writer.WriteEndElement();

    writer.WriteStartElement("br");
    writer.WriteEndElement();

    writer.WriteString(description);

    writer.WriteEndElement(); // end the "p" element
  }
}
\end{verbatim}
\section{Explaining the code}
The Main entry point to the new RSSReader uses the System.Net classes directly to setup a WebRequest. You also see the XmlTextWriter being constructed, turning on indenting so we get a nice readable output. Then the XmlReader and XmlWriter become arguments to a recursive descent RSS parser; the top level method is called RSSToHtml().
\\
The top level RSSToHtml() method first checks that we really have an RSS file, by checking the root element name. MoveToContent() is a convenient way of skipping the XML prolog and going right to the top level element in the document. If the XML document used namespaces, then we'd also want to match on the NamespaceUri property; however, this particular XML document doesn't use namespaces. If we find an $<$rss$>$ element, then we read the contents, calling ChannelToHtml() when we find a <channel> element and calling ItemToHtml() when we find an $<$item$>$ element. Any other element is skipped. This is all wrapped in the XmlWriter call to write the root level $<$html$>$ output element.
Comment on this article Have you tried C\# for XML applications? What about pull parsers versus SAX? Share your experience in our forum.
Post your comments
\\
The ChannelToHtml() method does two things: it writes out the HTML head element containing a $<$title$>$ element, then it writes out the HTML body. Notice here we can simply use the XmlWriter.WriteNode() method which copies the $<$title$>$ element from the input reader to the output, since an HTML $<$title$>$ is exactly the same as an RSS one. The HTML head element terminates when we reach the first child $<$item$>$ element or the $<$/channel$>$ EndElement token. In the HTML body we look for $<$item$>$ elements and call ItemToHtml().
\\
The ItemToHtml() method writes out an HTML $<$p$>$ tag, then reads the $<$title$>$, $<$link$>$ and $<$description$>$ elements out of the input. These input tags could arrive in any order, which is why we have to read them all before we can write the output. Once we have them we can write the $<$a$>$ tag, with $<$href attribute equal to the $<$link$>$ element, and content equal to the $<$title$>$, followed by an empty $<$br$>$ element and the description.
\\
All in all, it seems like a much simpler way to deal with converting RSS to HTML. .NET's built-in XML parser is pretty neat.
\section{Java Pull Parsers}
But pull parsers are not unique to the .NET world. The Java Community Process is currently working on a standard called StAX, the Streaming API for XML. This nascent API is, in turn, based upon several vendors' pull parser implementations, notably Apache's Xerces XNI, BEA's XML Stream API, XML Pull Parser 2, PullDOM  (for Python), and, yes, Microsoft's XmlReader.
\\
So how would we implement this same program in yet another pull parser, the Common API for XML Pull Parsing, or XPP? Let's take a look.
\begin{verbatim}
package com.xml;

import java.io.*;
import java.net.*;
import java.util.*;

import com.alexandriasc.xml.XMLWriter;
import org.xmlpull.v1.*;

public class RSSReader {

  public static void main(String [] args) {
    // create an instance of RSSReader
    RSSReader rssreader = new RSSReader();

    XMLWriter writer = null;
    try {
      String url = args[0];
      writer = new XMLWriter(new OutputStreamWriter(System.out),false);
      XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
      XmlPullParser parser = factory.newPullParser();
      InputStreamReader stream = new InputStreamReader(
        new URL(url).openStream());
      parser.setInput(stream);
      parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL,false);
      rssreader.RSSToHtml(parser, writer);
    } catch (Exception e) {
      e.printStackTrace(System.err);
    } finally {
      try {
        writer.flush();
      } catch (IOException io) {
        io.printStackTrace(System.err);
      }
    }
  }

  public void RSSToHtml(XmlPullParser parser, XMLWriter writer)
  throws IOException, XmlPullParserException {
    // equivalent to XmlReader.MoveToContent()
    while (parser.next() != XmlPullParser.START_TAG
      && !parser.getName().equals("rss")) {
    }
    if (parser.getName().equals("rss")) {
      writer.beginElement("html");
      do {
        parser.next();
        if (parser.getEventType() == XmlPullParser.START_TAG
          && parser.getName().equals("channel")) {
          ChannelToHtml(parser, writer);
        } else if (parser.getEventType() == XmlPullParser.START_TAG
          && parser.getName().equals("item")) {
          ItemToHtml(parser, writer);
        }
      } while (parser.getEventType() != XmlPullParser.END_DOCUMENT);
      writer.endElement();
    } else {
      // not an RSS document!
    }
  }

  void ChannelToHtml(XmlPullParser parser, XMLWriter writer)
  throws IOException, XmlPullParserException {
    writer.beginElement("head");
    // scan header elements and pick out the title.
    while (!(parser.next() == XmlPullParser.END_TAG
      && parser.getName().equals("channel"))) {
      if (parser.getEventType() == XmlPullParser.START_TAG) {
        do {
          if (parser.getEventType() == XmlPullParser.START_TAG
            && parser.getName().equals("title")) {
            while (parser.next() != XmlPullParser.END_TAG) {
              if (parser.getEventType() == XmlPullParser.TEXT) {
                writer.writeElement("title",null,parser.getText());
                break;
              }
            }
            break;
          }
        } while (parser.next() != XmlPullParser.END_TAG);
        break;
      }
    }
    writer.endElement();

    writer.beginElement("body");
    // transform the items.
    do {
      if (parser.getEventType() == XmlPullParser.START_TAG 
        && parser.getName().equals("item")) {
        ItemToHtml(parser, writer);
      }
      parser.next();
    } while (parser.getEventType() != XmlPullParser.END_DOCUMENT);
    writer.endElement();
  }

  void ItemToHtml(XmlPullParser parser, XMLWriter writer)
  throws IOException, XmlPullParserException {
    writer.beginElement("p");

    String title = null, link = null, description = null;
    while (parser.next() != XmlPullParser.END_DOCUMENT
      && parser.getEventType() != XmlPullParser.END_TAG) {
      if (parser.getEventType() == XmlPullParser.START_TAG
        && parser.getName().equals("title")) {
        if (parser.next() == XmlPullParser.TEXT)
          title = parser.readText();
      } else if (parser.getEventType() == XmlPullParser.START_TAG
        && parser.getName().equals("link")) {
        if (parser.next() == XmlPullParser.TEXT)
          link = parser.readText();
      } else if (parser.getEventType() == XmlPullParser.START_TAG
        && parser.getName().equals("description")) {
        if (parser.next() == XmlPullParser.TEXT)
          description = parser.readText();
      }
    }
    HashMap attributes = new HashMap(1);
    attributes.put("href", link);
    writer.beginElement("a",attributes);
    writer.write(title);
    writer.endElement();

    writer.writeEmptyElement("br");

    writer.write(description);

    writer.endElement(); // end the "p" element
  }
}
\end{verbatim}
Most of our port was the reverse of our previous ports; for example, changing Console.Out to System.out, making method names start with lowercase letters, adding explicit throws clauses. The real meat of this port is in two areas.
\section{The Parser}
First, we're using XmlPullParser as a rough equivalent of XmlTextReader. One difference is that while we are able to instantiate an XmlTextReader directly in C\# (remember, Microsoft is a one-stop shop), we have to use the Java XmlPullParserFactory to get a concrete implementation of the XmlPullParser interface. This should be a familiar exercise for anyone who's used JAXP or, for that matter, JDBC.
\\
Once we have the parser, most of the method name equivalencies are obvious. Remember that in C\# the == operator works just fine for strings, but in Java you must use the .equals() method; otherwise you'll be comparing object references rather than their values, not at all what we want to do. Also, you can't use a String as the expression in a switch...case statement in Java, so we've turned those into an if...else structure.
\\
Another difference between the .NET XmlReader and the Java XmlPullParser has to do with the way in which events are pulled out of the XMLdocument. In the former, the ReadString() method will return all the text for the current element; while in the latter, next() must explicitly be called to position the parser at the text node before calling getText() or readText() to read the text.
\\
This may be a minor difference, but it tends to make our port a little more difficult. To better handle this requirement, I've changed several while loops into do...while loops. This, unfortunately, makes it less than a simple port; the logic has changed, but not considerably.
\section{The Writer}
Second, there is no XmlTextWriter in Java, so we're using Alexandria Software Consulting's XmlHelper package, which contains a class called XMLWriter. Besides the naming of methods, XMLWriter operates almost identically to .NET's XmlWriter, except for two details.
\\
First, XMLWriter has the notion of a collection of attributes, whereas XmlWriter requires you to write each attribute individually. In Java, we call beginElement(), passing the name and the Map of attributes, whereas in C\#, we called WriteStartElement() followed by WriteAttributeString().
\\
Second, XMLWriter has a writeEmptyElement() method, where XmlWriter requires you to call WriteStartElement() followed by WriteEndElement(). However, .NET automatically collapses an empty element into a short end element (in this case, $<$br /$>$). .NET's way gives you the flexibility of determining whether the element is empty at runtime. If, however, you need to force an end tag, you can call WriteFullEndElement() instead of WriteEndElement().
\section{Conclusion}
A pull parser makes it much easier to process XML, especially when you are processing XML with a well-defined grammar like RSS. This code is much easier to understand and maintain since there's no complex state machine to build or maintain. In fact, this code is completely stateless; the pull parser keeps track of all the state for us. So in that sense a pull parser is a higher level way of processing XML than SAX.
\\
Although my original code quite intentionally didn't do any error handling, error handling in a push model state machine adds even more complexity to an already complex model. The new RSSReader has clear placeholders for error handling code in the cases when the input doesn't comply with the expected RSS DTD.
\\
Performance can be an important consideration in an XML parser. Notice the call to Skip() (in the C\# version) when we find elements we're not interested in. In this case the XML parser can skip over entire subtrees of XML without having to call us back on every element, even ones we know we're not interested in. In this case we skip over the $<$image$>$ elements and all their children. Second, in C\# we could optimize out all the element name string comparisons and make the atomized pointer comparisons if we used the XmlReader's NameTable to pre-atomize those strings.
\\
Finally, using an XML writer makes our output generation more robust. For example, it will correctly convert special characters $-$$-$ $<$, \&, etc. $-$$-$ into their respective entity references. Because it maintains its own state internally, it never forgets which element to close after a convoluted series of while loops. And it will always produce XML output in the consistent and readable format of your choice.
\\
And now for the inevitable comparison between .NET's XmlReader/XmlWriter and the equivalent functionality in Java. As usual, I'll say that in .NET, Microsoft has provided it all for you and, thus, it is undeniably simpler to learn and use. The C\# version of our RSSReader is about 20\% shorter than the Java version, which is great unless you work in one of those shops which still measures productivity in KLOCs. And the readability of the code itself is much greater in C\#, although that probably can be chalked up at least in part to my own lack of skill in that conversion from while to do...while.
\\
But the real bottom line remains that doing it the .NET way means that Microsoft provides all the standards-compliant tools that 90\% of developers are likely to need, while the Java way still means putting together a solution from various pieces that you can scrounge from various sources. Some of those pieces come from the Java Community Process and thus represent peer-reviewed, formally approved APIs, but some come from a quick search of the Web, and in the end only you are qualified to judge their worthiness
\appendix
\section{Appendix : Learning C\# XML}
In his opening keynote at the IDEAlliance XML 2001 Conference in Orlando, Florida, in December, James Clark said: "Just because it comes from Microsoft, it's not necessarily bad". With that in mind, I decided to explore what C\# has to offer to the Java-XML community.
\\
I've been watching the continuing Microsoft story with a vague combination of intrigue and apprehension. You almost certainly know by now that, due to an awkward combination of hubris and court orders, Microsoft has stopped shipping any Java implementation with Windows, choosing instead to hitch its wagon to a star of its own making, C\#.
\\
As a consumer, I'm not sure whether I like Microsoft's business practices. As a software developer, however, I'm interested in learning new languages and technologies. I've read enough to see that C\# is enough like Java to make an interesting porting project. Even if I never write another line of C\# code, there is certainly a lot to be learned from how Microsoft has integrated XML into its .NET platform.
\\
In this series I'll be porting a few small XML applications, which I've hypothetically written in Java, to C\# in order to see if I can improve my Java programming.
The Exercises
\\
The first Java application to port to C\#, which I call RSSReader, does something that most XML programmers have done at some point: read in an RSS stream using SAX and convert it to HTML. For our purposes, I'll expect to be reading an RSS 1.0 stream using JAXP and outputing out an HTML stream using java.io classes. We'll see that this example ports nicely to the C\# XmlReader class.
\\
Future examples will convert JDOM to the C\# XmlDocument and XmlNode classes, as well as experimenting with ports from an XML databinding framework to ADO.NET. There's a lot to ADO.NET, and I'll discuss some of that as well.
\\
Here's our first Java program, RSSReader, which was adapted from Sun's JAXP Tutorial. I've stripped out some of the error handling and such for the sake of simplicity.
\begin{verbatim}
package com.xml;

import java.io.*;
import java.util.Stack;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

public class RSSReader extends DefaultHandler {

  static private Writer out;
  static String lineEnd =  System.getProperty("line.separator");

  Stack stack = new Stack();
  StringBuffer value = null;
  String title = null;
  String link = null;
  String desc = null;

  public static void main(String args []) {
    // create an instance of RSSReader
    DefaultHandler handler = new RSSReader();

    try {
      // Set up output stream
      out = new OutputStreamWriter(System.out, "UTF8");

      // get a SAX parser from the factory
      SAXParserFactory factory = SAXParserFactory.newInstance();
      SAXParser saxParser = factory.newSAXParser();

      // parse the document from the parameter
      saxParser.parse(args[0], handler);

    } catch (Exception t) {
      System.err.println(t.getClass().getName());
      t.printStackTrace(System.err);
    }
  }

  public void startDocument() throws SAXException {
    emit("<html>" + lineEnd);
  }

  public void endDocument() throws SAXException {
    emit("</html>" + lineEnd);
  }

  public void startElement(String namespaceURI,String sName,
    String qName,Attributes attrs) throws SAXException {
    String eName = sName; // element name
    if ("".equals(eName)) eName = qName; // namespaceAware = false
    stack.push(eName);
    value = new StringBuffer();
  }

  public void endElement(String namespaceURI,String sName,String qName)
    throws SAXException {
    String eName = (String)stack.pop();
    if (eName.equals("title") && stack.peek().equals("channel")) {
      emit("  <head>" + lineEnd);
      emit("  <title>" + value + "</title>" + lineEnd);
      emit("  </head>" + lineEnd);
      emit("  <body>" + lineEnd);
    } else if (eName.equals("title") &&
           stack.peek().equals("item")) {
      title = null == value ? "" : value.toString();
    } else if (eName.equals("link") &&
           stack.peek().equals("item")) {
      link = null == value ? "" : value.toString();
    } else if (eName.equals("description") &&
           stack.peek().equals("item")) {
      desc = null == value ? "" : value.toString();
    } else if (eName.equals("item")) {
      emit("  <p><a href=\"" + link + "\">" +
        title + "</a><br>" + lineEnd);
      emit("   " + desc + "</p>" + lineEnd);
    } else if (eName.equals("channel")) {
      emit("  </body>" + lineEnd);
    }
    value = null;
  }

  public void characters(char buf [], int offset, int len)
    throws SAXException {
    String s = new String(buf, offset, len);
    value.append(s);
  }

  private static void emit(String s) throws SAXException {
    try {
      out.write(s);
      out.flush();
    } catch (IOException e) {
      throw new SAXException("I/O error", e);
    }
  }
}
\end{verbatim}
Compiling and running this program, we get the following results when we try to read XMLhack.com's RSS 1.0 feed (some lines have been wrapped for legibility):
\begin{verbatim}
C:\> java com.xml.RSSReader http://xmlhack.com/rss.php
<html>
  <head>
  <title>xmlhack</title>
  </head>
  <body>
  <p><a href="http://www.xmlhack.com/read.php?item=1511">Activity
around the Dublin Core</a><br>
   The Dublin Core Metadata Initiative (DCMI) has seen a
recent spate of activity,   Recent publications include The
Namespace Policy for the Dublin Core Metadata Initiative, 

Expressing Simple Dublin Core in RDF/XML, 

and Expressing Qualified Dublin Core in RDF/XML.</p>
  ...
\end{verbatim}
\subsection{An Intense and Bracing Introduction to C\#}
The most important thing to remember about C\# is that although it's definitely not Java, it is a lot like Java. So much so that you can probably port a lot of your application logic with a simple search-and-replace operation. (I'll highlight those areas when we go over the relevant code examples.) There are a few syntactic differences between the languages; I'll mention a couple of the most interesting ones, but rather than go over them in detail, you should read about the language on your own. I recommend any or all of the following articles: Conversational C\# for Java Programmers by Raffi Krikorian; C\#: A language alternative or just J--? by Mark Johnson; and A Comparison of Microsoft's C\# Programming Language to Sun Microsystems' Java Programming Language by Dare Obasanjo. There are also several books on C\#, including Programming C\# by Jesse Liberty and the forthcoming C\# In A Nutshell by Peter Drayton \& Ben Albahari. And of course, there is Microsoft's own invaluable .NET Framework Class Library.
\\
Let's begin our introduction to C\# by diving right into the code.
\\
The first change really is a simple global search-and-replace. The C\# equivalent of the System.out class is Console, and the println() method's equivalent is WriteLine().
\\
What we call packages in Java are called assemblies in C\# -- an oversimplification, but it will do for purposes -- and they are brought into scope not with the import statement but with the using statement. Also, rather than declaring what package your class belongs to with a package statement, the assembly is declared through nested braces.
\\
You don't have to be a rocket scientist to know that there is no javax.xml.parsers assembly in C\#. Microsoft has provided an assembly called System.Xml which contains all the XML classes you're likely to need.
\\
The last of our simple changes is one that may take some getting used to. Every Java developer knows that method names begin with lowercase letters, right? Well, Microsoft has taken a different route: building on its MFC naming tradition, all method names in C\# begin with capital letters, including Main().
\\
Now let's see what those changes have done to our sample code so far.
\begin{verbatim}
using System;

public class com {
  public class xml {
  public class RSSReader {

    static private Writer out;
    static String lineEnd =  System.getProperty("line.separator");

    Stack stack = new Stack();
    StringBuffer value = null;
    String title = null;
    String link = null;
    String desc = null;

    public static void Main(String args[]) {

    // don't worry about the rest of this code yet
    // ...
    }
  }
  }
}
\end{verbatim}
\subsection{Compiling Your Code}
Since we've made our trivial changes, we might as well let the compiler tell us what else we have to do. Yes, I know there's going to be a lot of error messages, but we have to start somewhere.
\\
Of course you know the command line to compile and run the Java version of RSSReader (I'm running this on Windows, since that's where my C\# project lives):
\begin{verbatim}
javac -g -classpath %CLASSPATH% com\xml\RSSReader.java
\end{verbatim}
Here is the equivalent C\# compilation command line:
\begin{verbatim}
csc /debug /r:System.Xml.dll /t:exe RSSReader.cs
\end{verbatim}
Just as javac is the Java Compiler, csc is the Cee-Sharp Compiler (get it?). I've listed the parameters in the same respective order in each compile line, so that you can see that the C\# equivalent of -g is /debug and the Java -classpath parameter (which was not strictly necessary in this example, of course) is something like the /r switch. There's a final parameter on the C\# compile line, /t:exe. In C\#, you can run your code in the .NET runtime or you can compile an executable directly. In this case I'm compiling an executable, saving the wonderful world of .NET for future articles.
\\
If you run the compile line, you'll get a list of errors like the following.
\begin{verbatim}
Microsoft (R) Visual C\# .NET Compiler version 7.00.9372.1
for Microsoft (R) .NET Framework version 1.0.3328
Copyright (C) Microsoft Corporation 2001. All rights reserved.

RSSReader1.cs(7,29): error CS1519:
	Invalid token 'out' in class, struct, or interface member
declaration
RSSReader1.cs(16,42): error CS1552:
	Array type specifier, [], must appear before parameter
name
...
\end{verbatim}
Which should give you the impression that, although the languages are very similar, they're not identical. Maybe it's best not to try to compile it just yet. We're going to need to cover some minor syntax points first.
\subsection{C\# Minutiae}
We need to make three very simple changes. First, out is a reserved word in C\#, and, in fact, we should just delete that line and change all instances of out.write() to Console.Write().
\\
Second, in Java the brackets of an array declaration may come either after the type or after the instance name. In our case, the Java code is written as String args[]. In C\#, the brackets must come after the type, thusly: String [] args. Another simple fix in two places.
\\
Finally, several of our Java methods have throws clauses. In C\#, every exception is a runtime exception; there is no throws concept. We'll just delete all the throws clauses.
\\
Compile again, and you'll see these errors:
\begin{verbatim}
Microsoft (R) Visual C\# .NET Compiler version 7.00.9372.1
for Microsoft (R) .NET Framework version 1.0.3328
Copyright (C) Microsoft Corporation 2001. All rights reserved.

RSSReader1.cs(9,7): error CS0246:
	The type or namespace name 'Stack' could not be found
  (are you missing a using directive or an assembly reference?)
RSSReader1.cs(10,7): error CS0246:
	The type or namespace name 'StringBuffer' could not 
  be found (are you missing a using directive or an
  assembly reference?)
RSSReader1.cs(48,22): error CS0246:
	The type or namespace name 'Attributes' could not be
  found (are you missing a using directive or an assembly
  reference?)
\end{verbatim}
Which raises two more issues. Some of the Java classes that you've come to know and love don't exist in the C\# world, though similar assemblies are available. And some do exist, but you've got to bring those assemblies into scope with the using statement. So our solution to these errors is twofold.
\\
First, add the following lines at the top of your C\# file.
\begin{verbatim}
using System;
using System.Collections;
using System.IO;
using System.Text;
\end{verbatim}
Second, in addition to C\# method names starting with a capital letter, there's another perversity: string starts with a lower case letter because it's actually a C\# primitive. There are also some minor differences in the names of utility classes. For example, instead of StringBuffer, C\# has a StringBuilder class. Try to suppress your gag reflex, and do some global search-and-replaces.
\\
These changes will fix more compilation errors. And now we can learn about XmlReader.
\subsection{XmlReader}
System.Xml contains many very useful classes. When you're talking about stream-based XML parsing, however, XmlReader is what you want..
\\
XmlReader is most analogous to SAX, although it does not require implementing an interface as most SAX implementations do (as JAXP's does). Instead, you simply instantiate a concrete XmlReader of your choice -- there are several to choose from: XmlTextReader and XmlStreamReader are the most useful -- then call its Read() method and pick nodes off as they are returned to you.
\\
In algorithmic terms, we could say that SAX uses callbacks, while XmlReader uses an event loop. An event loop is an infinite loop, during which certain events are received and dispatched to you. Internally, SAX may well have the same sort of event loop, but the callback methods hide that particular detail from you. A callback method is simply invoked when SAX's parser comes across an event.
\\
While that means that we don't need all those callback methods to satisfy an interface, we can still use them to dig through the nodes that we've read. In fact, we're just retrofitting a SAX-like API on top of XmlReader. Here's the new version of the code; be sure and compare these to the Java version above.
\begin{verbatim}
public static void Main(string [] args) {
  // create an instance of RSSReader
  RSSReader reader = new RSSReader();

  // parse the document from the parameter
  reader.Parse(args[0]);
}

// we have to write this method ourselves, since it's
// not provided by the API
public void Parse(string url) {
  try {
    XmlTextReader reader = new XmlTextReader(url);
    while (reader.Read()) {
      switch (reader.NodeType) {
      case XmlNodeType.Document:
        StartDocument();
        break;
      case XmlNodeType.Element:
        string namespaceURI = reader.NamespaceURI;
        string name = reader.Name;
        Hashtable attributes = new Hashtable();
        if (reader.HasAttributes) {
          for (int i = 0; i < reader.AttributeCount; i++) {
            reader.MoveToAttribute(i);
            attributes.Add(reader.Name,reader.Value);
          }
        }
        StartElement(namespaceURI,name,name,attributes);
        break;
      case XmlNodeType.EndElement:
        EndElement(reader.NamespaceURI,
               reader.Name, reader.Name);
        break;
      case XmlNodeType.Text:
        Characters(reader.Value,0,reader.Value.Length);
        break;
      // There are many other types of nodes, but
      // we are not interested in them
      }
    }
  } catch (XmlException e) {
    Console.WriteLine(e.Message);
  }
}

...

public void StartElement(string namespaceURI,string sName,
             string qName,Hashtable attrs) {
  string eName = sName; // element name
  if ("".Equals(eName))
    eName = qName; // namespaceAware = false
  stack.Push(eName);
  value = new StringBuilder();
}
\end{verbatim}
You'll notice that XmlReader has several interesting members, including NodeType, NamespaceURI, Name, and Value. NodeType can have several values depending on the node read from the XML document; we're only interested in Element, EndElement, and Text, though there are several other node types that you can use: CDATA, ProcessingInstruction, Comment, XmlDeclaration, Document, DocumentType, and EntityReference.
\\
As we receive one of the NodeTypes we're interested in, we read the Name or Value and hand it off to our callback methods.
\\
One other point to note is that SAX has an Attributes class, for which the closest equivalent in C\# is XmlAttributeCollection. However, creating one of those is a more complex task than I really want to deal with right now, so instead, I've changed StartElement's last parameter to a Hashtable. I've done some fancy processing to populate it, but we're not interested in attributes for this example so I won't go into any more detail about it.
\\
Now that we've got our event loop and our "callback" methods in place, we can port the RSS-to-HTML conversion logic from Java to C\#. This isn't exactly rocket science either, as the syntax of C\# is basically the same as Java. Don't forget, though: all method names start with capital letters.
\\
Here is the complete source listing for RSSReader.cs:
\begin{verbatim}
using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Xml;

public class com {
  public class xml {
    public class RSSReader {
      static String LineEnd = "\n";

      Stack stack = new Stack();
      StringBuilder value = null;
      string title = null;
      string link = null;
      string desc = null;

      public static void Main(string [] args) {
        // create an instance of RSSReader
        RSSReader reader = new RSSReader();

        // parse the document from the parameter
        reader.Parse(args[0]);
      }

      // we have to write this method ourselves, since it's
      // not provided by the API
      public void Parse(string url) {
        try {
          XmlTextReader reader = new XmlTextReader(url);
          while (reader.Read()) {
            switch (reader.NodeType) {
            case XmlNodeType.Document:
              StartDocument();
              break;
            case XmlNodeType.Element:
              string namespaceURI = reader.NamespaceURI;
              string name = reader.Name;
              Hashtable attributes = new Hashtable();
              if (reader.HasAttributes) {
                for (int i = 0; i < reader.AttributeCount; i++) {
                  reader.MoveToAttribute(i);
                  attributes.Add(reader.Name,reader.Value);
                }
              }
              StartElement(namespaceURI,name,name,attributes);
              break;
            case XmlNodeType.EndElement:
              EndElement(reader.NamespaceURI,
                     reader.Name, reader.Name);
              break;
            case XmlNodeType.Text:
              Characters(reader.Value,0,reader.Value.Length);
              break;
            // There are many other types of nodes, but
            // we are not interested in them
            }
          }
        } catch (XmlException e) {
          Console.WriteLine(e.Message);
        }
      }

      public void StartDocument() {
        Emit("<html>" + LineEnd);
      }

      public void EndDocument() {
        Emit("</html>" + LineEnd);
      }

      public void StartElement(string namespaceURI,string sName,
                   string qName,Hashtable attrs) {
        string eName = sName; // element name
        if ("".Equals(eName)) eName = qName; // namespaceAware = false
        stack.Push(eName);
        value = new StringBuilder();
      }

      public void EndElement(string namespaceURI,string sName,
                   string qName) {
        string eName = (string)stack.Pop();
        if (eName.Equals("title") &&
            stack.Peek().Equals("channel")) {
          Emit("  <head>" + LineEnd);
          Emit("  <title>" + value + "</title>" + LineEnd);
          Emit("  </head>" + LineEnd);
          Emit("  <body>" + LineEnd);
        } else if (eName.Equals("title") &&
                   stack.Peek().Equals("item")) {
          title = null == value ? "" : value.ToString();
        } else if (eName.Equals("link") &&
                   stack.Peek().Equals("item")) {
          link = null == value ? "" : value.ToString();
        } else if (eName.Equals("description") &&
                   stack.Peek().Equals("item")) {
          desc = null == value ? "" : value.ToString();
        } else if (eName.Equals("item")) {
          Emit("  <p><a href=\"" + link + "\">" +
                title + "</a><br>" + LineEnd);
          Emit("   " + desc + "</p>" + LineEnd);
        } else if (eName.Equals("channel")) {
          Emit("  </body>" + LineEnd);
        }
        value = null;
      }

      public void Characters(string buf, int offset, int len) {
        value.Append(buf);
      }

      private static void Emit(string s) {
        Console.Write(s);
      }
    }
  }
}
\end{verbatim}
Try it, and you'll see that it compiles without a problem. Run it like so (output wrapped for legibility):
\begin{verbatim}
C:\> RSSReader http://xmlhack.com/rss.php
  <head>
  <title>xmlhack</title>
  </head>
  <body>
  <p><a href="http://www.xmlhack.com/read.php?item=1511">Activity
around the Dublin Core</a><br>
   The Dublin Core Metadata Initiative (DCMI) has seen a recent
spate of activity,
	Recent publications include The Namespace Policy for the
Dublin Core Metadata
	Initiative, Expressing Simple Dublin Core in RDF/XML, and
Expressing Qualified
	Dublin Core in RDF/XML.</p>
...
\end{verbatim}
This should look very familiar, as it's exactly the same output that our Java program produced. You might have seen a completely different result, however, like this:
\begin{verbatim}
C:\> RSSReader http://xmlhack.com/rss.php

Unhandled Exception: System.Security.SecurityException:
    Request for the permission of type
	System.Net.WebPermission, System, Version=1.0.3300.0,
    Culture=neutral, PublicKeyToken=b77a5c561934e089 failed.
   at System.Security.CodeAccessSecurityEngine.CheckHelper(
    PermissionSet grantedSet,
    PermissionSet deniedSet, CodeAccess
    Permission demand, PermissionToken permToken)
   at System.Security.CodeAccessSecurityEngine.Check(
    PermissionToken permToken,
    CodeAccessPermission demand, StackCrawlMark& stackMark,
    Int32 checkFrames, Int32 unrestrictedOverride)
   at System.Security.CodeAccessSecurityEngine.Check(
    CodeAccessPermission cap, StackCrawlMark& stackMark)
   at System.Security.CodeAccessPermission.Demand()
   at System.Net.HttpRequestCreator.Create(Uri Uri)
   at System.Net.WebRequest.Create(Uri requestUri, 
    Boolean useUriBase)
   at System.Net.WebRequest.Create(Uri requestUri)
   at System.Xml.XmlDownloadManager.GetNonFileStream(Uri uri,
    ICredentials credentials)
   at System.Xml.XmlDownloadManager.GetStream(Uri uri,
    ICredentials credentials)
   at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri,
    String role, Type ofObjectToReturn)
   at System.Xml.XmlTextReader.CreateScanner()
   at System.Xml.XmlTextReader.Init()
   at System.Xml.XmlTextReader.Read()
   at RSSReader.Parse(String url) in U:\thing\RSSReader.cs:line 31
   at RSSReader.Main(String[] args) in U:\thing\RSSReader.cs:line 23
\end{verbatim}
The state of the failed permission was:
\begin{verbatim}
<IPermission class="System.Net.WebPermission, System,
    Version=1.0.3300.0, Culture=neutral, 
    PublicKeyToken=b77a5c561934e089" version="1">
   <ConnectAccess>
    <URI uri="http://xmlhack\.com/rss\.php"/>
   </ConnectAccess>
</IPermission>
\end{verbatim}
This happens because C\# will not load an assembly on a network drive. Just move your RSSReader.exe executable onto a local driveand try again.
Conclusions
\\
What can we learn from XmlReader? First of all, unlike Java's XML libraries, all of System.Xml is provided by Microsoft. This means that, among other things, there is a consistent interface and a consistent set of tools for all your XML needs. No need to shop around for parsers and SAX implementations.
\\
That can also be considered a drawback. Since Java has multiple implementations, you're free to use the one that fits best. And with the advent of JAXP, you can drop in the different implementations without changing your code at all. Doing that in C\# is, well, impossible; you're stuck with the one, true Microsoft way.
\\
As far as the XmlReader event model, it doesn't seem that SAX has much to learn at all. You'll remember that we actually had to write some additional code when we ported the program to XmlReader because SAX provided the event loop for us; with SAX, all we have to do is write the callbacks.
\\
On the other hand, System.Xml does provide some nifty classes like XmlAttribute (which we didn't really discuss here) and XmlNodeType, which give you very convenient, standard ways to look at attributes and nodes, instead of having to deal with strings and Hashtables and such. And all these classes are used throughout C\#'s XML facilities.
\\
If you don't want to write either an event loop or callbacks, the read-only, forward-only, stream-based model might not be for you; you might prefer a whole-document model (like, say, DOM). In that case, XmlReader will not appeal to you any more than SAX does. There is another set of tools in C\#, starting with XmlDocument, which we'll discuss in the next article, which gives you all the power of a document stored in memory, plus the added convenience of building on what you've already learned.
\appendix
http://www.xml.com/lpt/a/972
\end{document}
